/*

Miranda IM: the free IM client for Microsoft* Windows*

Copyright 2000-2007 Miranda ICQ/IM project, 
all portions of this codebase are copyrighted to the people 
listed in contributors.txt.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
aLONG with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

#include "commonheaders.h"

namespace NIcoLib {

typedef struct TIcoDesc {
	DWORD	dwHash;
	LPCSTR	pszName;
	LPTSTR	pszDesc;
	LPTSTR	pszSection;
	WORD	idResource;
	BYTE	size;
} ICODESC;

HMODULE hIconDll = NULL;
HICON	hEmptyIcon = NULL;

static ICODESC icoDesc[] = 
{
	// common
	{ 0, ICO_COMMON_IM,			_T("IM Naming"),				SECT_COMMON,	IDI_MIRANDA,			0	},
	{ 0, ICO_COMMON_PASSWORD,	_T("Password"),					SECT_COMMON,	IDI_PASSWORD,			0	},
	{ 0, ICO_COMMON_FEMALE,		_T("Female"),					SECT_COMMON,	IDI_FEMALE,				0	},
	{ 0, ICO_COMMON_MALE,		_T("Male"),						SECT_COMMON,	IDI_MALE,				0	},
	{ 0, ICO_COMMON_BIRTHDAY,	_T("Birthday"),					SECT_COMMON,	IDI_BIRTHDAY,			0	},
	{ 0, ICO_COMMON_CLOCK,		_T("Timezone"),					SECT_COMMON,	IDI_CLOCK,				1	},
	{ 0, ICO_COMMON_MARITAL,	_T("Marital status"),			SECT_COMMON,	IDI_MARITAL,			0	},
	{ 0, ICO_COMMON_ADDRESS,	_T("Address"),					SECT_COMMON,	IDI_TREE_ADDRESS,		0	},
	{ 0, ICO_COMMON_ANNIVERSARY,_T("Anniversary"),				SECT_COMMON,	IDI_ANNIVERSARY,		0	},

	// lists
	{ 0, ICO_LST_MODULES,		_T("Export: Modules"),			SECT_COMMON,	IDI_LST_MODULES,		0	},
	{ 0, ICO_LST_FOLDER,		_T("Export: Folder"),			SECT_COMMON,	IDI_LST_FOLDER,			0	},

	{ 0, ICO_ZOD_AQUARIUS,		_T("Aquarius"),					SECT_COMMON,	IDI_ZOD_AQUARIUS,		1	},
	{ 0, ICO_ZOD_ARIES,			_T("Aries"),					SECT_COMMON,	IDI_ZOD_ARIES,			1	},
	{ 0, ICO_ZOD_CANCER,		_T("Cancer"),					SECT_COMMON,	IDI_ZOD_CANCER,			1	},
	{ 0, ICO_ZOD_CAPRICORN,		_T("Capricorn"),				SECT_COMMON,	IDI_ZOD_CAPRICORN,		1	},
	{ 0, ICO_ZOD_GEMINI,		_T("Gemini"),					SECT_COMMON,	IDI_ZOD_GEMINI,			1	},
	{ 0, ICO_ZOD_LEO,			_T("Leo"),						SECT_COMMON,	IDI_ZOD_LEO,			1	},
	{ 0, ICO_ZOD_LIBRA,			_T("Libra"),					SECT_COMMON,	IDI_ZOD_LIBRA,			1	},
	{ 0, ICO_ZOD_PISCES,		_T("Pisces"),					SECT_COMMON,	IDI_ZOD_PISCES,			1	},
	{ 0, ICO_ZOD_SAGITTARIUS,	_T("Sagittarius"),				SECT_COMMON,	IDI_ZOD_SAGITTARIUS,	1	},
	{ 0, ICO_ZOD_SCORPIO,		_T("Scorpio"),					SECT_COMMON,	IDI_ZOD_SCORPIO,		1	},
	{ 0, ICO_ZOD_TAURUS,		_T("Taurus"),					SECT_COMMON,	IDI_ZOD_TAURUS,			1	},
	{ 0, ICO_ZOD_VIRGO,			_T("Virgo"),					SECT_COMMON,	IDI_ZOD_VIRGO,			1	},

	// dialogs
	{ 0, ICO_DLG_DETAILS,		_T("Details Infobar"),			SECT_DLG,		IDI_DLG_DETAILS,		48	},
	{ 0, ICO_DLG_PHONE,			_T("Phone Infobar"),			SECT_DLG,		IDI_DLG_PHONE,			1	},
	{ 0, ICO_DLG_EMAIL,			_T("E-Mail Infobar"),			SECT_DLG,		IDI_DLG_EMAIL,			1	},
	{ 0, ICO_DLG_EXPORT,		_T("Export VCard"),				SECT_DLG,		IDI_EXPORT,				1	},
	{ 0, ICO_DLG_IMPORT,		_T("Import VCard"),				SECT_DLG,		IDI_IMPORT,				1	},
	{ 0, ICO_DLG_ANNIVERSARY,	_T("Anniversary Infobar"),		SECT_DLG,		IDI_ANNIVERSARY,		1	},

	// button icons
	{ 0, ICO_BTN_UPDATE,		_T("Update"),					SECT_BUTTONS,	IDI_BTN_UPDATE,			0	},
	{ 0, ICO_BTN_IMPORT,		_T("Import"),					SECT_BUTTONS,	IDI_IMPORT,				0	},
	{ 0, ICO_BTN_EXPORT,		_T("Export"),					SECT_BUTTONS,	IDI_EXPORT,				0	},
	{ 0, ICO_BTN_OK,			_T("Ok"),						SECT_BUTTONS,	IDI_BTN_OK,				0	},
	{ 0, ICO_BTN_CANCEL,		_T("Cancel"),					SECT_BUTTONS,	IDI_BTN_CLOSE,			0	},
	{ 0, ICO_BTN_APPLY,			_T("Apply"),					SECT_BUTTONS,	IDI_BTN_APPLY,			0	},
	{ 0, ICO_BTN_GOTO,			_T("Goto"),						SECT_BUTTONS,	IDI_BTN_GOTO,			0	},
	{ 0, ICO_BTN_PHONE,			_T("Phone"),					SECT_BUTTONS,	IDI_BTN_PHONE,			0	},
	{ 0, ICO_BTN_FAX,			_T("Fax"),						SECT_BUTTONS,	IDI_BTN_FAX,			0	},
	{ 0, ICO_BTN_CELLULAR,		_T("Cellular"),					SECT_BUTTONS,	IDI_BTN_CELLULAR,		0	},
	{ 0, ICO_BTN_CUSTOMPHONE,	_T("Custom Phone"),				SECT_BUTTONS,	IDI_BTN_CUSTOMPHONE,	0	},
	{ 0, ICO_BTN_EMAIL,			_T("e-mail"),					SECT_BUTTONS,	IDI_BTN_EMAIL,			0	},
	{ 0, ICO_BTN_DOWNARROW,		_T("Down arrow"),				SECT_BUTTONS,	IDI_BTN_DOWNARROW,		0	},
	{ 0, ICO_BTN_ADD,			_T("Add"),						SECT_BUTTONS,	IDI_BTN_ADD,			0	},
	{ 0, ICO_BTN_EDIT,			_T("Edit"),						SECT_BUTTONS,	IDI_BTN_EDIT,			0	},
	{ 0, ICO_BTN_DELETE,		_T("Delete"),					SECT_BUTTONS,	IDI_BTN_DELETE,			0	},
	{ 0, ICO_BTN_SEARCH,		_T("Search"),					SECT_BUTTONS,	IDI_SEARCH,				0	},
	{ 0, ICO_BTN_EXIMPORT,		_T("Ex-/Import"),				SECT_BUTTONS,	IDI_BTN_EXIMPORT,		0	},
	{ 0, ICO_BTN_BDAY_BACKUP,	_T("Backup Birthday"),			SECT_BUTTONS,	IDI_BTN_BIRTHDAY_BACKUP,0	},
		
	{ 0, ICO_RMD_DTB0,			_T("Birthday today"),			SECT_REMIND,	IDI_RMD_DTB0,			0	},
	{ 0, ICO_RMD_DTB1,			_T("Birthday tomorrow"),		SECT_REMIND,	IDI_RMD_DTB1,			0	},
	{ 0, ICO_RMD_DTB2,			_T("Birthday in 2 days"),		SECT_REMIND,	IDI_RMD_DTB2,			0	},
	{ 0, ICO_RMD_DTB3,			_T("Birthday in 3 days"),		SECT_REMIND,	IDI_RMD_DTB3,			0	},
	{ 0, ICO_RMD_DTB4,			_T("Birthday in 4 days"),		SECT_REMIND,	IDI_RMD_DTB4,			0	},
	{ 0, ICO_RMD_DTB5,			_T("Birthday in 5 days"),		SECT_REMIND,	IDI_RMD_DTB5,			0	},
	{ 0, ICO_RMD_DTB6,			_T("Birthday in 6 days"),		SECT_REMIND,	IDI_RMD_DTB6,			0	},
	{ 0, ICO_RMD_DTB7,			_T("Birthday in 7 days"),		SECT_REMIND,	IDI_RMD_DTB7,			0	},
	{ 0, ICO_RMD_DTB8,			_T("Birthday in 8 days"),		SECT_REMIND,	IDI_RMD_DTB8,			0	},
	{ 0, ICO_RMD_DTB9,			_T("Birthday in 9 days"),		SECT_REMIND,	IDI_RMD_DTB9,			0	},
	{ 0, ICO_RMD_DTBX,			_T("Birthday later"),			SECT_REMIND,	IDI_RMD_DTBX,			0	},
	
	{ 0, ICO_RMD_DTA0,			_T("Anniversary today"),		SECT_REMIND,	IDI_RMD_DTA0,			0	},
	{ 0, ICO_RMD_DTA1,			_T("Anniversary tomorrow"),		SECT_REMIND,	IDI_RMD_DTA1,			0	},
	{ 0, ICO_RMD_DTA2,			_T("Anniversary in 2 days"),	SECT_REMIND,	IDI_RMD_DTA2,			0	},
	{ 0, ICO_RMD_DTA3,			_T("Anniversary in 3 days"),	SECT_REMIND,	IDI_RMD_DTA3,			0	},
	{ 0, ICO_RMD_DTA4,			_T("Anniversary in 4 days"),	SECT_REMIND,	IDI_RMD_DTA4,			0	},
	{ 0, ICO_RMD_DTA5,			_T("Anniversary in 5 days"),	SECT_REMIND,	IDI_RMD_DTA5,			0	},
	{ 0, ICO_RMD_DTA6,			_T("Anniversary in 6 days"),	SECT_REMIND,	IDI_RMD_DTA6,			0	},
	{ 0, ICO_RMD_DTA7,			_T("Anniversary in 7 days"),	SECT_REMIND,	IDI_RMD_DTA7,			0	},
	{ 0, ICO_RMD_DTA8,			_T("Anniversary in 8 days"),	SECT_REMIND,	IDI_RMD_DTA8,			0	},
	{ 0, ICO_RMD_DTA9,			_T("Anniversary in 9 days"),	SECT_REMIND,	IDI_RMD_DTA9,			0	},
	{ 0, ICO_RMD_DTAX,			_T("Anniversary later"),		SECT_REMIND,	IDI_RMD_DTAX,			0	},

	//{ 0, ICO_EMPTY,				_T(""),							SECT_COMMON,	100,					0	}
};


/**
 * name:	SortProc()
 * desc:	used for sorting the tab pages
 *
 * return:	-1 or 0 or 1
 **/
static INT SortProc(ICODESC* item1, ICODESC* item2)
{
	if(item1 && item2) {
		if(item2->dwHash > item1->dwHash) return -1;
		if(item2->dwHash < item1->dwHash) return 1;
	}
	return 0;
}

/**
 * name:	FileExists
 * desc:	converts a relative filepath to absolute and checks whether the file exists or not
 * param:	pszRelativeFileName	- the relative path to convert
 *			pszAbsoluteFileName - the converted absolute path
 * return:	TRUE if file was found, FALSE otherwise
 **/
static BOOLEAN FileExists(LPSTR pszRelativeFileName, LPSTR pszAbsoluteFileName)
{
	INT len;
	
	if(pszRelativeFileName && *pszRelativeFileName && pszAbsoluteFileName &&
	  (len = CallService(MS_UTILS_PATHTOABSOLUTE, (WPARAM)pszRelativeFileName, (LPARAM)pszAbsoluteFileName)) > 0) 
	{
		HANDLE hFile = CreateFileA(pszAbsoluteFileName,
			GENERIC_READ, FILE_SHARE_READ,
			NULL, OPEN_EXISTING,
			FILE_ATTRIBUTE_NORMAL,
			NULL);
		
		if(hFile != INVALID_HANDLE_VALUE) {
			CloseHandle(hFile);
			return TRUE;
		}
	}
	return FALSE;
}

/**
 * name:	GetDefaultIconFileName
 * desc:	find the default iconpack file and return its path
 * param:	szFileName	- pointer to a buffer that receives the path (must at least have MAX_PATH chars)
 * return:	TRUE if file was found, FALSE otherwise
 **/
BOOLEAN GetDefaultIconFileName(LPSTR pszAbsoluteFileName)
{
	return (
		FileExists("Customize\\Icons\\uinfoex_icons.dll", pszAbsoluteFileName) ||
		FileExists("Icons\\uinfoex_icons.dll", pszAbsoluteFileName) ||
		FileExists("Plugins\\uinfoex_icons.dll", pszAbsoluteFileName)
	);
}

/**
 * name:	LoadIconPack
 * desc:	load the specified iconpack
 * param:	szFileName	- full qualified path to the iconfile to load
 * return:	HMODULE on success, NULL otherwise
 **/
HMODULE LoadIconPack(LPSTR pszAbsoluteFileName)
{
	HMODULE hIconDll = LoadLibraryA(pszAbsoluteFileName);
	CHAR szFileVersion[64];

	// check iconpack's fileversion
	if(hIconDll) { 
		if(DBGetByte(SET_ICONS_CHECKFILEVERSION, TRUE) && 
			(
				!LoadStringA(hIconDll, IDS_ICOPACKVERSION, szFileVersion, sizeof(szFileVersion)) ||
				mir_strcmp(szFileVersion, "__UserInfoEx_IconPack_1.2__")
			)
		  )
		{
			MsgErr(NULL, _T("Warning: Your current IconPack's version differs from the one UserInfoEx is designed for.\nSome icons may not be displayed correctly"));
		}
	}
	return hIconDll;
}

/**
 * name:	GetDefaultIconFileName
 * desc:	find the default iconpack file and return its path
 * param:	szFileName	- pointer to a buffer that receives the path (must at least have MAX_PATH chars)
 * return:	TRUE if file was found, FALSE otherwise
 **/
HMODULE GetDefaultIconFile(LPSTR szFileName)
{
	if(GetDefaultIconFileName(szFileName)) {
		return LoadIconPack(szFileName);
	}
	return NULL;
}

/**
 * name:	GetIcon
 * desc:	returns a icon, identified by a name or recource id
 * param:	pszIcon	- name of the icon
 *			idIcon	- if we do not use icolib, it may identify a resource id of the default iconpack file
 *			cx, cy	- tells wich size the icon should have
 * return:	HICON if the icon is loaded, NULL otherwise
 **/
HICON GetIcon(LPCSTR pszIcon, INT idIcon, WORD cx, WORD cy)
{
	// use icolib to manage icons
	if(myGlobals.HaveIcoLib) {
		if(!pszIcon) return NULL;
		return (HICON)CallService(MS_SKIN2_GETICON, NULL, (LPARAM)pszIcon);
	}

	// user does not have icolib installed, so read the icons from the default
	// iconpack file
	if(!hIconDll) return NULL;

	// find the icon by its string in the icoDesc array
	if(idIcon == 0) {
		ICODESC key, *pIco;
		if(!pszIcon) return NULL;

		// calculate hash for icon to find
		key.dwHash = hashSetting(pszIcon);
		// quicksearch
		pIco = (ICODESC*)bsearch(&key, icoDesc, SIZEOF(icoDesc), sizeof(icoDesc[0]),
							(INT (*)(const VOID*, const VOID*))SortProc);
		if(pIco) {
			idIcon = pIco->idResource;
			if(cx == 0 || cy == 0) {
				switch(pIco->size) {
					case 0:
						cx = GetSystemMetrics(SM_CXSMICON);
						cy = GetSystemMetrics(SM_CYSMICON);
						break;
					case 1:
						cx = GetSystemMetrics(SM_CXICON);
						cy = GetSystemMetrics(SM_CYICON);
						break;
					default:
						cx = cy = pIco->size;
						break;
				}
			}
		}
		if(idIcon == 0) return NULL;
	}
	// default iconsize is 16x16
	else
	if(cx == 0 || cy == 0) {
		cx = GetSystemMetrics(SM_CXSMICON);
		cy = GetSystemMetrics(SM_CYSMICON);
	}
	return (HICON)LoadImage(hIconDll, MAKEINTRESOURCE(idIcon), IMAGE_ICON, cx, cy, 0);
}

/**
 * name:	SetCtrlIcons
 * desc:	set the icon of each control in the list
 * param:	hDlg	 - handle to the dialog control, that owns the controls
 *			pCtrl	 - list to all controls and its icon names
 *			numCtrls - number of elements in the pCtrl list
 * return:	nothing
 **/
VOID SetCtrlIcons(HWND hDlg, const ICONCTRL* pCtrl, BYTE numCtrls)
{
	HICON	hIcon;
	BYTE	i;
	HWND	hCtrl;

	for(i = 0; i < numCtrls; i++) {
		hIcon = GetIcon(pCtrl[i].pszIcon);
		if(hIcon == hEmptyIcon) hIcon = NULL;
		if(pCtrl[i].idCtrl)	{
			hCtrl = GetDlgItem(hDlg, pCtrl[i].idCtrl);
			switch(pCtrl[i].Message) {
				case STM_SETICON:
				case STM_SETIMAGE:
					SendMessage(hCtrl, STM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
					ShowWindow(hCtrl, hIcon ? SW_SHOW : SW_HIDE);
					break;
				case BM_SETIMAGE:
					SendMessage(hCtrl, BM_SETIMAGE, IMAGE_ICON, (WPARAM)hIcon);
					break;
			}
		}
		else
			SendMessage(hDlg, pCtrl[i].Message, ICON_BIG, (LPARAM)hIcon);
	}
}

/**
 * name:	LoadModule
 * desc:	add default icons to the skin library or load customized icons
 * param:	none
 * return:	nothing
 **/
VOID LoadModule()
{
	CHAR szFileName[MAX_PATH];
	WORD i;

	*szFileName = 0;

	// check if to use icolib
	if(!ServiceExists(MS_SKIN2_ADDICON)) {
		myGlobals.HaveIcoLib = FALSE;
		hIconDll = GetDefaultIconFile(szFileName);
		if(!hIconDll) {
			MsgErr(NULL, _T("Warning! The IconPack was not found!"));
			return;
		}
		// initiate hashes for faster finding
		for(i = 0; i < SIZEOF(icoDesc); i++) {
			icoDesc[i].dwHash = hashSetting(icoDesc[i].pszName);
		}
		// prepare quicksearch
		qsort(icoDesc, SIZEOF(icoDesc), sizeof(icoDesc[0]), (INT (*)(const VOID*, const VOID*))SortProc);
	}
	else {
		SKINICONDESC sid;
		TCHAR szDesc[MAXSETTING];

		myGlobals.HaveIcoLib = TRUE;
		GetDefaultIconFileName(szFileName);
		sid.cbSize = sizeof(SKINICONDESC);
		sid.pszDefaultFile = szFileName;
		sid.flags = SIDF_TCHAR;
		sid.hDefaultIcon = NULL;
		sid.ptszDescription = szDesc;
		// init default icons
		for(i = 0; i < SIZEOF(icoDesc); i++) {
			sid.pszName = (LPSTR)icoDesc[i].pszName;
			sid.ptszSection = TranslateTS(icoDesc[i].pszSection);
			sid.iDefaultIndex = ICONINDEX(icoDesc[i].idResource);
			switch(icoDesc[i].size) {
				case 0:
					sid.cx = GetSystemMetrics(SM_CXSMICON);
					sid.cy = GetSystemMetrics(SM_CYSMICON);
					break;
				case 1:
					sid.cx = GetSystemMetrics(SM_CXICON);
					sid.cy = GetSystemMetrics(SM_CYICON);
					break;
				default:
					sid.cx = sid.cy = icoDesc[i].size;
					break;
			}
			// display required size in description text
			mir_sntprintf(szDesc, SIZEOF(szDesc), _T("%s\n(%ux%u)"), TranslateTS(icoDesc[i].pszDesc), sid.cx, sid.cy);
			CallService(MS_SKIN2_ADDICON, SIDF_TCHAR, (LPARAM)&sid);	
		}
		hEmptyIcon = GetIcon(ICO_EMPTY);
	}
}

} // namespace IcoLib
